package com.irenty.effectivej.ch02.objectreation;

import java.io.Serializable;
import java.util.logging.Logger;

import org.testng.annotations.Test;

/*
 * Item 3:  Enforce the singleton property with a private constructor or an enum type
 */
public class SingletonTest {
	private final static Logger LOGGER = Logger.getLogger(SingletonTest.class
			.getName());

	private static class Singleton1 {
		public static final Singleton1 INSTANCE = new Singleton1();

		private Singleton1() {
			// should throw an exception to protect contructor from access via
			// reflection AccessibleObject.setAccessible
		}

		public void say() {
			LOGGER.info("Singleton1 is saying Hello!");
		}
	}

	@Test
	public void testSingleton1() {
		Singleton1.INSTANCE.say();
	}

	/*
	 * Serialization. To maintain the singleton guarantee, you have to declare
	 * all instance fields transient and provide a readResolve method.
	 * Otherwise, each time a serialized instance is deserialized, a new
	 * instance will be created
	 */
	private static class Singleton2 implements Serializable {

		private static final long serialVersionUID = 3991941816478007929L;
		private static final Singleton2 INSTANCE = new Singleton2();

		private Singleton2() {
		}

		public static Singleton2 getInstance() {
			return INSTANCE;
		}

		// readResolve method to preserve singleton property
		private Object readResolve() {
			// Return the one Singleton and let the garbage collector
			// take care of the deserialized one
			return INSTANCE;
		}

		public void say() {
			LOGGER.info("Singleton2 is saying Hello!");
		}
	}

	@Test
	public void testSingleton2() {
		Singleton2.getInstance().say();
	}

	/*
	 * Enum singleton - the preferred approach provides the serialization
	 * machinery for free, and provides an ironclad guarantee against multiple
	 * instantiation, even in the face of sophisticated serialization or
	 * reflection attacks.
	 */
	public enum Singleton3 {
		INSTANCE;
		public void say() {
			LOGGER.info("Singleton3 is saying Hello!");
		}
	}

	@Test
	public void testSingleton3() {
		Singleton3.INSTANCE.say();
	}

}
